package com.janke.softballnexus.client;

import java.util.Set;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.web.bindery.requestfactory.gwt.client.RequestFactoryEditorDriver;
import com.google.web.bindery.requestfactory.shared.Receiver;
import com.google.web.bindery.requestfactory.shared.Request;
import com.google.web.bindery.requestfactory.shared.RequestContext;
import com.google.web.bindery.requestfactory.shared.Violation;
import com.janke.softballnexus.client.events.UpdateDivision;
import com.janke.softballnexus.client.widgets.DivisionEditor;
import com.janke.softballnexus.shared.DivisionProxy;
import com.janke.softballnexus.shared.SoftballRequestFactory;
import com.janke.softballnexus.shared.SoftballRequestFactory.DivisionRequest;

public class DivisionEditorWorkflow extends Composite {

	interface Binder extends UiBinder<DialogBox, DivisionEditorWorkflow> {
		Binder BINDER = GWT.create(Binder.class);
	}

	interface Driver extends
			RequestFactoryEditorDriver<DivisionProxy, DivisionEditor> {
	}

	static void register(EventBus eventBus,
			final SoftballRequestFactory requestFactory) {
		eventBus.addHandler(UpdateDivision.TYPE, new UpdateDivision.Handler() {
			public void startEdit(DivisionProxy division,
					RequestContext requestContext) {
				new DivisionEditorWorkflow(requestFactory, division)
						.edit(requestContext);
			}
		});
	}

	@UiField
	HTMLPanel contents;

	@UiField
	DialogBox dialog;

	@UiField(provided = true)
	DivisionEditor divisionEditor;

	private Driver editorDriver;
	private DivisionProxy division;
	private final SoftballRequestFactory requestFactory;

	private DivisionEditorWorkflow(SoftballRequestFactory requestFactory,
			DivisionProxy division) {
		this.requestFactory = requestFactory;
		this.division = division;
		divisionEditor = new DivisionEditor();
		Binder.BINDER.createAndBindUi(this);
		contents.addDomHandler(new KeyUpHandler() {
			public void onKeyUp(KeyUpEvent event) {
				if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
					onCancel(null);
				}
			}
		}, KeyUpEvent.getType());
	}

	@UiHandler("cancel")
	void onCancel(ClickEvent event) {
		dialog.hide();
	}

	@UiHandler("save")
	void onSave(ClickEvent event) {
		// Flush the contents of the UI
		RequestContext context = editorDriver.flush();

		// Check for errors
		if (editorDriver.hasErrors()) {
			dialog.setText("Errors detected locally");
			return;
		}

		((DivisionRequest) context).persist().using(division);

		// Send the request
		context.fire(new Receiver<Void>() {
			@Override
			public void onViolation(Set<Violation> errors) {
				dialog.setText("Errors detected on the server");
				editorDriver.setViolations(errors);
			}

			@Override
			public void onSuccess(Void response) {
				// If everything went as planned, just dismiss the dialog box
				dialog.hide();
			}
		});
	}

	private void edit(RequestContext requestContext) {
		editorDriver = GWT.create(Driver.class);
		editorDriver.initialize(requestFactory, divisionEditor);

		if (requestContext == null) {
			fetchAndEdit();
			return;
		}

		editorDriver.edit(division, requestContext);
		divisionEditor.focus();
		dialog.center();
	}

	private void fetchAndEdit() {
		// The request is configured arbitrarily
		Request<DivisionProxy> fetchRequest = requestFactory.find(division
				.stableId());

		// Add the paths that the EditorDrives computes
		fetchRequest.with(editorDriver.getPaths());

		// We could do more with the request, but we just fire it
		fetchRequest.to(new Receiver<DivisionProxy>() {
			@Override
			public void onSuccess(DivisionProxy division) {
				DivisionEditorWorkflow.this.division = division;
				// Start the edit process
				DivisionRequest context = requestFactory.divisionRequest();
				// Display the UI
				edit(context);
				// Configure the method invocation to be sent in the context
				context.persist().using(division);
				// The context will be fire()'ed from the onSave() method
			}
		}).fire();
	}
}
